package net.sevear.chaos.download
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.net.SharedObject;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.utils.ByteArray;
	import flash.utils.Timer;
	import flash.utils.setTimeout;
	
	import net.sevear.chaos.event.KeeperEvent;
	import net.sevear.chaos.event.SourceProxyEvent;
	import net.sevear.chaos.utils.DataUtil;
	import net.sevear.chaos.utils.Queue;
	import net.sevear.chaos.utils.SongID3InfoEidtor;
	import net.sevear.chaos.view.ChaosLogPanel;
	import net.sevear.chaos.vo.TrackInfo;
	
	public class DBRadioKeeper extends EventDispatcher
	{
		public static const SOURCE_DB_RADIO	:int = 0;
		public static const SOURCE_SINA    	:int = 1;
		public static const SOURCE_GOOGLE 	:int = 2;
		public static const SOURCE_XIAMI	:int = 3;
		
		public static const DOWN_Q_MAXLEN:int = 10;
		
		private static const MAX_TRY_TIMES:int = 3;
		
		private var so:SharedObject;
		private var downQ:Queue;
		private var isSendingRequest:Boolean;
		private var loader:URLLoader;
		private var _curInfo:TrackInfo;
		private var _enable:Boolean = false;
		private var _sina:Boolean = false;
		private var _google:Boolean = false;
		private var _dir:String;
		private var id3Editor:SongID3InfoEidtor;
		private var downloadSource:int = SOURCE_DB_RADIO;
		private var timer:Timer;
		
		private var tryTimes:int;
		
		private var sinaProxy:SinaYuekuProxy;
		private var googleProxy:GoogleMusicProxy;
		
		private var waitTime:int;
		private static const MAX_WAIT_TIME:int = 90;
		public function DBRadioKeeper(target:IEventDispatcher=null)
		{
			super(target);
			downQ = new Queue(DOWN_Q_MAXLEN);
			timer = new Timer(1000);
			timer.addEventListener(TimerEvent.TIMER,onTimer);
			initSharedObj();
			initURLLoader();
			initGoogleProxy();
			initSinaProxy();
			id3Editor = new SongID3InfoEidtor();
		}
		
		private static var _instance:DBRadioKeeper;
		public static function getInstance():DBRadioKeeper{
			if(_instance == null){
				_instance = new DBRadioKeeper();
			}
			return _instance;
		}
		
		public function set enable(value:Boolean):void{
			if(_enable!=value){
				_enable = value;
				so.data.enableDown = value.toString();
				so.flush();
				if(value == false){
					stopQueue();
				}else{
					startQueue();
				}
				
				dispatchEvent(new KeeperEvent(KeeperEvent.KEEPER_ENABLE_CHANGED));
			}
		}
		
		public function get enable():Boolean{
			return _enable;
		}
		
		
		public function set sinaEnable(value:Boolean):void{
			_sina = value;
			so.data.enableSina = value.toString();
			so.flush();
		}
		
		public function get sinaEnable():Boolean{
			return _sina;
		}
		
		public function set googleEnable(value:Boolean):void{
			_google = value;
			so.data.enableGoogle = value.toString();
			so.flush();
		}
		
		public function get googleEnable():Boolean{
			return _google;
		}
		
		public function get source():int{
			return downloadSource;
		}
		
		public function downSong(info:TrackInfo):void{
			if(_enable ){
				if(downQ.isFull()==false){
					downQ.push(info);
					startQueue();
				}
			}
		}
		
		public function downLoadFinished():Boolean{
			return downQ.isEmpty() && isSendingRequest == false;
		}
		
		//FIXME:
		public function reTryDownLoad(info:TrackInfo):void{
			downSong(info);
		}
		
		private function startQueue():void{
			if(timer.running==false) 
				timer.start();
		}
		
		private function stopQueue():void{
			if(timer.running) timer.stop();
			try{
				loader.close();
			}catch(e:Error){
			}
			if(_curInfo!=null){
				if(_google) googleProxy.stopSearching();
				if(_sina)sinaProxy.stopSearching();
			}
			isSendingRequest = false;
			waitTime=0;
		}
		
		private function initSharedObj():void{
			so = SharedObject.getLocal("douchaos","/");
			
			if(so.data.saveDir!=null){
				saveDir = so.data.saveDir as String;
			}else{
				so.data.saveDir = "";
				so.flush();
			}
			
			if(so.data.enableDown!=null 
				&& so.data.enableSina!=null 
				&& so.data.enableGoogle!=null){
				_enable = so.data.enableDown == "true";
				_sina = so.data.enableSina == "true" && _enable;
				_google = so.data.enableGoogle == "true" && _enable;
			}else{
				_enable = false;
				_sina = false;
				_google = false;
				so.data.enableDown = "false";
				so.data.enableSina = "false";
				so.data.enableGoogle = "false";
				so.flush();
			}
		}
		
		private function initURLLoader():void{
			loader = new URLLoader();
			loader.dataFormat = URLLoaderDataFormat.BINARY;
			loader.addEventListener(IOErrorEvent.IO_ERROR,onIoError);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
			loader.addEventListener(Event.COMPLETE,onComplete);
			loader.addEventListener(ProgressEvent.PROGRESS,onProgress);
		}
		
		private function initGoogleProxy():void{
			googleProxy = new GoogleMusicProxy(DBRadioKeeper.SOURCE_GOOGLE);
			googleProxy.addEventListener(SourceProxyEvent.FOUND_URL,onGoogleFound);
			googleProxy.addEventListener(SourceProxyEvent.NOT_FOUND_URL,onGoogleNotFound);
			googleProxy.addEventListener(SourceProxyEvent.SERVICE_BREAK_DOWN,onGoogleNotFound);
			googleProxy.addEventListener(SourceProxyEvent.TIME_OUT,onGoogleNotFound);
		}
		
		private function initSinaProxy():void{
			sinaProxy = new SinaYuekuProxy();
			sinaProxy.addEventListener(SourceProxyEvent.FOUND_URL,onSinaFound);
			sinaProxy.addEventListener(SourceProxyEvent.NOT_FOUND_URL,onSinaNotFound);
			sinaProxy.addEventListener(SourceProxyEvent.SERVICE_BREAK_DOWN,onSinaNotFound);
			sinaProxy.addEventListener(SourceProxyEvent.TIME_OUT,onSinaNotFound);
		}
		
		private function dispatchEventWithTrack(type:String):void{
			var newTrack:TrackInfo = new TrackInfo();
			DataUtil.generateNewTrack(newTrack,_curInfo);
			dispatchEvent(new KeeperEvent(type,newTrack));
		}
		
		private function processDownQ():void{
			waitTime = 0;
			if(downQ.isEmpty()==false){
				_curInfo = downQ.pop() as TrackInfo;
				if(checkDuplicateFile(_curInfo.name,saveDir)==true){
					dispatchEventWithTrack(KeeperEvent.TRACK_FILE_EXISTED);
				}else{
					isSendingRequest = true;
					//because the quality of  mp3 from sina yueku is better than the same one from Douban FM
					if(_google){
						downloadSource = SOURCE_GOOGLE;
						googleProxy.searchSongNew(_curInfo);
						ChaosLogPanel.getInstance().addTempBasicInfo("正在用谷歌音乐搜索 "+curInfo.name);
					}
					else if(_sina)
					{
						downloadSource = SOURCE_SINA;
						sinaProxy.searchSongNew(_curInfo);
						ChaosLogPanel.getInstance().addTempBasicInfo("正在用新浪乐库搜索 "+curInfo.name);
					}
					else 
						downFromListenSource();
				}
			}else{
				stopQueue();
				dispatchEvent(new KeeperEvent(KeeperEvent.LIST_FINISHED));
			}
		}
		
		private function onGoogleFound(e:SourceProxyEvent):void{
			_curInfo.downURLs[SOURCE_GOOGLE] = e.downURL;
			loader.load(new URLRequest(e.downURL));
			ChaosLogPanel.getInstance().addTempBasicInfo(curInfo.name+"开始从谷歌音乐下载...");
		}
		
		private function onGoogleNotFound(e:SourceProxyEvent):void{
			if(_sina)
			{
				downloadSource = SOURCE_SINA;
				sinaProxy.searchSongNew(_curInfo);
				ChaosLogPanel.getInstance().addTempBasicInfo("正在用新浪乐库搜索 "+curInfo.name);
			}
			else 
				downFromListenSource();
		}
		
		private function onSinaFound(e:SourceProxyEvent):void{
			_curInfo.downURLs[SOURCE_SINA] = e.downURL;
			loader.load(new URLRequest( e.downURL));
			ChaosLogPanel.getInstance().addTempBasicInfo(curInfo.name+"开始从新浪乐库下载...");
		}
		
		private function onSinaNotFound(e:SourceProxyEvent):void{
			downFromListenSource();
		}
		
		private function onTimer(e:TimerEvent):void{
			if(waitTime>=MAX_WAIT_TIME){
				//等待其他下载源的时间太长了
				dispatchEventWithTrack(KeeperEvent.TIMEOUT_ERROR);
				stopQueue();
				startQueue();
			}else{
				waitTime++;
				if(isSendingRequest == false)
				{
					processDownQ();
				}
			}
		}
		
		
		private function tryDownLoadMp3():void{
			if(downloadSource == SOURCE_GOOGLE && _curInfo.downURLs[SOURCE_GOOGLE]!=null){
				loader.load(new URLRequest(_curInfo.downURLs[SOURCE_GOOGLE]));
			}
			else if(downloadSource == SOURCE_DB_RADIO && _curInfo.downURLs[SOURCE_DB_RADIO]!=null){
				var rst:URLRequest = new URLRequest(_curInfo.downURLs[SOURCE_DB_RADIO]);
				var head:URLRequestHeader = new URLRequestHeader();
				head.name = "Referer"
				head.value = "http://douban.fm/swf/52000/radioplayer.swf";
				rst.requestHeaders.push(head);
				loader.load(rst);
			}else if(downloadSource == SOURCE_SINA && _curInfo.downURLs[SOURCE_SINA]){
				loader.load(new URLRequest(_curInfo.downURLs[SOURCE_SINA]));
			}else if(downloadSource == SOURCE_XIAMI && _curInfo.downURLs[SOURCE_XIAMI]){
				loader.load(new URLRequest(_curInfo.downURLs[SOURCE_XIAMI]));
			}
		}
		
		private function downFromListenSource():void{
			if(_curInfo.downURLs[SOURCE_DB_RADIO]!=null){
				downFromDB();
			}else if(_curInfo.downURLs[SOURCE_XIAMI]!=null){
				downFromXiami();
			}
		}
		
		private function downFromDB():void{
			downloadSource = SOURCE_DB_RADIO;
			if(_curInfo.downURLs[SOURCE_DB_RADIO] != null){
				var rst:URLRequest = new URLRequest(_curInfo.downURLs[SOURCE_DB_RADIO]);
				var head:URLRequestHeader = new URLRequestHeader();
				head.name = "Referer"
				head.value = "http://douban.fm/swf/52000/radioplayer.swf";
				rst.requestHeaders.push(head);
				loader.load(rst);
				ChaosLogPanel.getInstance().addTempBasicInfo(_curInfo.name+"开始从豆瓣电台下载...");
			}
		}
		
		private function downFromXiami():void{
			downloadSource = SOURCE_XIAMI;
			if(_curInfo.downURLs[SOURCE_XIAMI] != null){
				loader.load(new URLRequest(_curInfo.downURLs[SOURCE_XIAMI]));
			}
		}
		
		//TODO:the same track name does not mean the same track
		private function checkDuplicateFile(songName:String,localDir:String):Boolean{
			var fl:File = File.desktopDirectory.resolvePath(localDir+songName+".mp3");
			return fl.exists;
		}
		
		public function set saveDir(value:String):void{
			_dir = value.replace(/\\/g,'/');
			if(so!=null){
				so.data.saveDir = _dir;
				so.flush();
			}
		}
		
		public function get saveDir():String{
			return _dir;
		}
		
		public function get curInfo():TrackInfo{
			return _curInfo;
		}
		
		private function onIoError(e:IOErrorEvent = null):void{
			if (tryTimes >= MAX_TRY_TIMES)
			{
				tryTimes = 0;
				isSendingRequest = false;
				dispatchEventWithTrack(KeeperEvent.IO_ERROR);
			}else{
				tryTimes++;
				setTimeout(tryDownLoadMp3,2000);
			}
		}
		
		private function onSecurityError(e:SecurityErrorEvent):void{
			isSendingRequest = false;
		}
		
		private function onComplete(e:Event):void{
			saveSongFileToLocal(_curInfo.name,loader.data as ByteArray,_dir);
			isSendingRequest = false;
		}
		
		
		private function saveSongFileToLocal(songName:String,songContent:ByteArray,localDir:String):void{
			var fl:File = File.desktopDirectory.resolvePath(localDir+songName+".mp3");
			var fs:FileStream = new FileStream();
			try{
				fs.open(fl,FileMode.WRITE);
				fs.writeBytes(songContent);
				fs.close();
				if(downloadSource == SOURCE_DB_RADIO || downloadSource == SOURCE_XIAMI)
					id3Editor.updateID3(localDir+songName+".mp3",_curInfo);
				dispatchEventWithTrack(KeeperEvent.DOWN_TRACK_COMPLETE);
			}catch(e:Error){
				trace(e.message);
			}
			
			_curInfo = null;
		}
		
		private function onProgress(e:ProgressEvent):void{
			dispatchEvent(e);
		}
	}
}